use salvo::prelude::*;
use salvo::oapi::extract::*;
use serde::{Deserialize, Serialize};
use crate::{service, json_ok, JsonResult};
use crate::models::rbac::role::{Role, SafeRole};
use crate::service::rbac::role::RoleService;
use salvo::http::StatusError;

#[derive(Debug, Serialize)]
pub struct CreateRoleData {
    pub name: String,
    pub description: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct UpdateRoleData {
    pub name: String,
    pub description: Option<String>,
}

#[endpoint(tags("RBAC"))]
async fn create_role(req: &mut Request) -> Result<Json<SafeRole>, StatusError> {
    let db = service::conn();
    let role = req.parse_json::<Role>().await.map_err(|e| {
        StatusError::bad_request().brief(e.to_string())
    })?;
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req).map_err(|e| {
        StatusError::internal_server_error().brief(e.to_string())
    })?.unwrap_or(0);
    
    match RoleService::create(
        db, 
        &role.name, 
        role.description.as_deref(), 
        tenant_id
    ).await {
        Ok(created) => Ok(Json(SafeRole {
            id: created.id.unwrap(),
            name: created.name,
            description: created.description,
            tenant_id: created.tenant_id,
        })),
        Err(e) => Err(StatusError::internal_server_error().brief(e.to_string())),
    }
}

#[endpoint(tags("RBAC"), parameters(("role_id", description = "ID of the role to update")))]
async fn update_role(req: &mut Request) -> Result<Json<SafeRole>, StatusError> {
    let db = service::conn();
    let role_id = req.param::<String>("role_id").ok_or_else(|| {
        StatusError::bad_request().brief("Missing role_id parameter")
    })?;
    let update_data = req.parse_json::<Role>().await.map_err(|e| {
        StatusError::bad_request().brief(e.to_string())
    })?;
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req).map_err(|e| {
        StatusError::internal_server_error().brief(e.to_string())
    })?.unwrap_or(0);
    
    match RoleService::update(
        db, 
        &role_id, 
        Some(&update_data.name), 
        update_data.description.as_deref(),
        tenant_id
    ).await {
        Ok(updated) => Ok(Json(SafeRole {
            id: updated.id.unwrap(),
            name: updated.name,
            description: updated.description,
            tenant_id: updated.tenant_id,
        })),
        Err(e) => Err(StatusError::internal_server_error().brief(e.to_string())),
    }
}

#[endpoint(tags("RBAC"), parameters(("role_id", description = "ID of the role to delete")))]
async fn delete_role(req: &mut Request) -> Result<StatusCode, StatusError> {
    let db = service::conn();
    let role_id = req.param::<String>("role_id").ok_or_else(|| {
        StatusError::bad_request().brief("Missing role_id parameter")
    })?;
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req).map_err(|e| {
        StatusError::internal_server_error().brief(e.to_string())
    })?.unwrap_or(0);
    
    match RoleService::delete(db, &role_id, tenant_id).await {
        Ok(_) => Ok(StatusCode::NO_CONTENT),
        Err(e) => Err(StatusError::internal_server_error().brief(e.to_string())),
    }
}

#[endpoint(tags("RBAC"), parameters(("role_id", description = "ID of the role to retrieve")))]
async fn get_role(req: &mut Request, role_id: PathParam<String>) -> JsonResult<SafeRole> {
    let role_id = role_id.into_inner();
   
    if role_id.len() != 36 {
        return Err(StatusError::bad_request().into());
    }
    
    let db = service::conn();
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req).map_err(|e| {
        StatusError::internal_server_error().brief(e.to_string())
    })?.unwrap_or(0);
    match RoleService::find_by_id(db, &role_id, tenant_id).await {
        Ok(Some(role)) => json_ok(SafeRole {
            id: role.id,
            name: role.name,
            description: role.description,
            tenant_id: role.tenant_id,
        }),
        Ok(None) => Err(StatusError::not_found().into()),
        Err(e) => Err(StatusError::internal_server_error().into())
    }
}

#[endpoint(tags("RBAC"))]
async fn list_roles(req: &mut Request) -> Result<Json<Vec<SafeRole>>, StatusError> {
    let db = service::conn();
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req).map_err(|e| {
        StatusError::internal_server_error().brief(e.to_string())
    })?.unwrap_or(0);
    
    match RoleService::find_all(db, tenant_id).await {
        Ok(roles) => Ok(Json(roles.into_iter().map(|role| SafeRole {
            id: role.id.unwrap(),
            name: role.name,
            description: role.description,
            tenant_id: role.tenant_id,
        }).collect::<Vec<_>>())),
        Err(e) => Err(StatusError::internal_server_error().brief(e.to_string())),
    }
}

pub fn routes() -> Router {
    Router::with_path("roles")
        .post(create_role)
        .get(list_roles)
        .push(
            Router::with_path("{role_id}")
                .hoop(salvo::logging::Logger::new())
                .get(get_role)
                .put(update_role)
                .delete(delete_role)
        )
}
